xref: /freebsd/sys/dev/qcom_ess_edma/qcom_ess_edma_desc.c (revision 9f32893b05dabedc7f8332ec12e2a944b6543158)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/rman.h>
39 #include <sys/lock.h>
40 #include <sys/malloc.h>
41 #include <sys/mutex.h>
42 #include <sys/socket.h>
43 #include <sys/sockio.h>
44 
45 #include <net/if.h>
46 #include <net/if_var.h>
47 #include <net/if_media.h>
48 #include <net/ethernet.h>
49 
50 #include <machine/bus.h>
51 #include <machine/resource.h>
52 
53 #include <dev/fdt/fdt_common.h>
54 #include <dev/ofw/ofw_bus.h>
55 #include <dev/ofw/ofw_bus_subr.h>
56 
57 #include <dev/qcom_ess_edma/qcom_ess_edma_var.h>
58 #include <dev/qcom_ess_edma/qcom_ess_edma_reg.h>
59 #include <dev/qcom_ess_edma/qcom_ess_edma_hw.h>
60 #include <dev/qcom_ess_edma/qcom_ess_edma_desc.h>
61 #include <dev/qcom_ess_edma/qcom_ess_edma_debug.h>
62 
63 static void
qcom_ess_edma_desc_map_addr(void * arg,bus_dma_segment_t * segs,int nsegs,int error)64 qcom_ess_edma_desc_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs,
65     int error)
66 {
67 	if (error != 0)
68 		return;
69 	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
70 	*(bus_addr_t *)arg = segs[0].ds_addr;
71 }
72 
73 /*
74  * Initialise the given descriptor ring.
75  */
76 int
qcom_ess_edma_desc_ring_setup(struct qcom_ess_edma_softc * sc,struct qcom_ess_edma_desc_ring * ring,char * label,int count,int sw_desc_size,int hw_desc_size,int num_segments,int buffer_align)77 qcom_ess_edma_desc_ring_setup(struct qcom_ess_edma_softc *sc,
78     struct qcom_ess_edma_desc_ring *ring,
79     char *label,
80     int count,
81     int sw_desc_size,
82     int hw_desc_size,
83     int num_segments,
84     int buffer_align)
85 {
86 	int error;
87 	int hw_ring_size;
88 
89 	ring->label = strdup(label, M_TEMP);
90 	if (ring->label == NULL) {
91 		device_printf(sc->sc_dev,
92 		    "ERROR: failed to strdup label\n");
93 		error = ENOMEM;
94 		goto error;
95 	}
96 
97 	mtx_init(&ring->mtx, ring->label, NULL, MTX_DEF);
98 
99 	hw_ring_size = count * hw_desc_size;
100 
101 	/*
102 	 * Round the hardware ring size up to a cacheline
103 	 * so we don't end up with partial cacheline sizes
104 	 * causing bounce buffers to be used.
105 	 */
106 	hw_ring_size = ((hw_ring_size + PAGE_SIZE) / PAGE_SIZE) * PAGE_SIZE;
107 
108 	/*
109 	 * For now set it to 4 byte alignment, no max size.
110 	 */
111 	ring->ring_align = EDMA_DESC_RING_ALIGN;
112 	error = bus_dma_tag_create(
113 	    sc->sc_dma_tag,		/* parent */
114 	    EDMA_DESC_RING_ALIGN, 0,	/* alignment, boundary */
115 	    BUS_SPACE_MAXADDR,		/* lowaddr */
116 	    BUS_SPACE_MAXADDR,		/* highaddr */
117 	    NULL, NULL,			/* filter, filterarg */
118 	    hw_ring_size,		/* maxsize */
119 	    1,				/* nsegments */
120 	    hw_ring_size,		/* maxsegsize */
121 	    0,				/* flags */
122 	    NULL, NULL,			/* lockfunc, lockarg */
123 	    &ring->hw_ring_dma_tag);
124 	if (error != 0) {
125 		device_printf(sc->sc_dev,
126 		    "ERROR: failed to create descriptor DMA tag (%d)\n",
127 		    error);
128 		goto error;
129 	}
130 
131 	/*
132 	 * Buffer ring - used passed in value
133 	 */
134 	ring->buffer_align = buffer_align;
135 	error = bus_dma_tag_create(
136 	    sc->sc_dma_tag,		/* parent */
137 	    buffer_align, 0,		/* alignment, boundary */
138 	    BUS_SPACE_MAXADDR,		/* lowaddr */
139 	    BUS_SPACE_MAXADDR,		/* highaddr */
140 	    NULL, NULL,			/* filter, filterarg */
141 	    EDMA_DESC_MAX_BUFFER_SIZE * num_segments,	/* maxsize */
142 	    num_segments,			/* nsegments */
143 	    EDMA_DESC_MAX_BUFFER_SIZE,	/* maxsegsize */
144 	    0,				/* flags */
145 	    NULL, NULL,			/* lockfunc, lockarg */
146 	    &ring->buffer_dma_tag);
147 	if (error != 0) {
148 		device_printf(sc->sc_dev,
149 		    "ERROR: failed to create buffer DMA tag (%d)\n",
150 		    error);
151 		goto error;
152 	}
153 
154 	/*
155 	 * Allocate software descriptors
156 	 */
157 	ring->sw_desc = mallocarray(count, sw_desc_size, M_TEMP,
158 	    M_NOWAIT | M_ZERO);
159 	if (ring->sw_desc == NULL) {
160 		device_printf(sc->sc_dev,
161 		    "ERROR: failed to allocate sw_desc\n");
162 		goto error;
163 	}
164 
165 	/*
166 	 * Allocate hardware descriptors, initialise map, get
167 	 * physical address.
168 	 */
169 	error = bus_dmamem_alloc(ring->hw_ring_dma_tag,
170 	    (void **)&ring->hw_desc,
171 	     BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
172 	    &ring->hw_desc_map);
173 	if (error != 0) {
174 		device_printf(sc->sc_dev,
175 		    "failed to allocate DMA'able memory for hw_desc ring\n");
176 		goto error;
177 	}
178 	ring->hw_desc_paddr = 0;
179 	error = bus_dmamap_load(ring->hw_ring_dma_tag, ring->hw_desc_map,
180 	    ring->hw_desc, hw_ring_size, qcom_ess_edma_desc_map_addr,
181 	    &ring->hw_desc_paddr, BUS_DMA_NOWAIT);
182 	bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
183 	    BUS_DMASYNC_PREWRITE);
184 	QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_DESCRIPTOR_SETUP,
185 	    "%s: PADDR=0x%08lx\n", __func__, ring->hw_desc_paddr);
186 
187 	/*
188 	 * All done, initialise state.
189 	 */
190 	ring->hw_entry_size = hw_desc_size;
191 	ring->sw_entry_size = sw_desc_size;
192 	ring->ring_count = count;
193 
194 	return (0);
195 error:
196 	mtx_destroy(&ring->mtx);
197 	if (ring->label != NULL)
198 		free(ring->label, M_TEMP);
199 	if (ring->hw_desc != NULL) {
200 		bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
201 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
202 		bus_dmamap_unload(ring->hw_ring_dma_tag, ring->hw_desc_map);
203 		bus_dmamem_free(ring->hw_ring_dma_tag, ring->hw_desc,
204 		    ring->hw_desc_map);
205 		ring->hw_desc = NULL;
206 	}
207 	if (ring->sw_desc != NULL) {
208 		free(ring->sw_desc, M_TEMP);
209 		ring->sw_desc = NULL;
210 	}
211 	if (ring->hw_ring_dma_tag != NULL) {
212 		bus_dma_tag_destroy(ring->hw_ring_dma_tag);
213 		ring->hw_ring_dma_tag = NULL;
214 	}
215 	if (ring->buffer_dma_tag != NULL) {
216 		bus_dma_tag_destroy(ring->buffer_dma_tag);
217 		ring->buffer_dma_tag = NULL;
218 	}
219 
220 	return (error);
221 }
222 
223 /*
224  * Free/clean the given descriptor ring.
225  *
226  * The ring itself right now is static; so we don't free it.
227  * We just free the resources it has.
228  */
229 int
qcom_ess_edma_desc_ring_free(struct qcom_ess_edma_softc * sc,struct qcom_ess_edma_desc_ring * ring)230 qcom_ess_edma_desc_ring_free(struct qcom_ess_edma_softc *sc,
231     struct qcom_ess_edma_desc_ring *ring)
232 {
233 
234 	mtx_destroy(&ring->mtx);
235 	if (ring->label != NULL)
236 		free(ring->label, M_TEMP);
237 
238 	if (ring->hw_desc != NULL) {
239 		bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
240 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
241 		bus_dmamap_unload(ring->hw_ring_dma_tag, ring->hw_desc_map);
242 		bus_dmamem_free(ring->hw_ring_dma_tag, ring->hw_desc,
243 		    ring->hw_desc_map);
244 		ring->hw_desc = NULL;
245 	}
246 
247 	if (ring->sw_desc != NULL) {
248 		free(ring->sw_desc, M_TEMP);
249 		ring->sw_desc = NULL;
250 	}
251 
252 	if (ring->hw_ring_dma_tag != NULL) {
253 		bus_dma_tag_destroy(ring->hw_ring_dma_tag);
254 		ring->hw_ring_dma_tag = NULL;
255 	}
256 	if (ring->buffer_dma_tag != NULL) {
257 		bus_dma_tag_destroy(ring->buffer_dma_tag);
258 		ring->buffer_dma_tag = NULL;
259 	}
260 
261 	return (0);
262 }
263 
264 /*
265  * Fetch the given software descriptor pointer by index.
266  *
267  * Returns NULL if the index is out of bounds.
268  */
269 void *
qcom_ess_edma_desc_ring_get_sw_desc(struct qcom_ess_edma_softc * sc,struct qcom_ess_edma_desc_ring * ring,uint16_t index)270 qcom_ess_edma_desc_ring_get_sw_desc(struct qcom_ess_edma_softc *sc,
271     struct qcom_ess_edma_desc_ring *ring, uint16_t index)
272 {
273 	char *p;
274 
275 	if (index >= ring->ring_count)
276 		return (NULL);
277 
278 	p = (char *) ring->sw_desc;
279 
280 	return (void *) (p + (ring->sw_entry_size * index));
281 }
282 
283 /*
284  * Fetch the given hardware descriptor pointer by index.
285  *
286  * Returns NULL if the index is out of bounds.
287  */
288 void *
qcom_ess_edma_desc_ring_get_hw_desc(struct qcom_ess_edma_softc * sc,struct qcom_ess_edma_desc_ring * ring,uint16_t index)289 qcom_ess_edma_desc_ring_get_hw_desc(struct qcom_ess_edma_softc *sc,
290     struct qcom_ess_edma_desc_ring *ring, uint16_t index)
291 {
292 	char *p;
293 
294 	if (index >= ring->ring_count)
295 		return (NULL);
296 
297 	p = (char *) ring->hw_desc;
298 
299 	return (void *) (p + (ring->hw_entry_size * index));
300 }
301 
302 /*
303  * Flush the hardware ring after a write, before the hardware
304  * gets to it.
305  */
306 int
qcom_ess_edma_desc_ring_flush_preupdate(struct qcom_ess_edma_softc * sc,struct qcom_ess_edma_desc_ring * ring)307 qcom_ess_edma_desc_ring_flush_preupdate(struct qcom_ess_edma_softc *sc,
308     struct qcom_ess_edma_desc_ring *ring)
309 {
310 
311 	bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
312 	    BUS_DMASYNC_PREWRITE);
313 
314 	return (0);
315 }
316 
317 
318 /*
319  * Flush the hardware ring after the hardware writes into it,
320  * before a read.
321  */
322 int
qcom_ess_edma_desc_ring_flush_postupdate(struct qcom_ess_edma_softc * sc,struct qcom_ess_edma_desc_ring * ring)323 qcom_ess_edma_desc_ring_flush_postupdate(struct qcom_ess_edma_softc *sc,
324     struct qcom_ess_edma_desc_ring *ring)
325 {
326 
327 	bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
328 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
329 
330 	return (0);
331 }
332 
333 /*
334  * Get how many descriptor slots are available.
335  */
336 int
qcom_ess_edma_desc_ring_get_num_available(struct qcom_ess_edma_softc * sc,struct qcom_ess_edma_desc_ring * ring)337 qcom_ess_edma_desc_ring_get_num_available(struct qcom_ess_edma_softc *sc,
338     struct qcom_ess_edma_desc_ring *ring)
339 {
340 	uint16_t sw_next_to_fill;
341 	uint16_t sw_next_to_clean;
342 	uint16_t count = 0;
343 
344 	sw_next_to_clean = ring->next_to_clean;
345 	sw_next_to_fill = ring->next_to_fill;
346 
347 	if (sw_next_to_clean <= sw_next_to_fill)
348 		count = ring->ring_count;
349 
350 	return (count + sw_next_to_clean - sw_next_to_fill - 1);
351 }
352