]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/xdma/xdma.c
Upgrade Unbound to 1.6.1. More to follow.
[FreeBSD/FreeBSD.git] / sys / dev / xdma / xdma.c
1 /*-
2  * Copyright (c) 2016-2018 Ruslan Bukin <br@bsdpad.com>
3  * All rights reserved.
4  *
5  * This software was developed by SRI International and the University of
6  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
7  * ("CTSRD"), as part of the DARPA CRASH research programme.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include "opt_platform.h"
35 #include <sys/param.h>
36 #include <sys/conf.h>
37 #include <sys/bus.h>
38 #include <sys/kernel.h>
39 #include <sys/queue.h>
40 #include <sys/kobj.h>
41 #include <sys/malloc.h>
42 #include <sys/limits.h>
43 #include <sys/lock.h>
44 #include <sys/sysctl.h>
45 #include <sys/systm.h>
46 #include <sys/sx.h>
47
48 #include <machine/bus.h>
49
50 #ifdef FDT
51 #include <dev/fdt/fdt_common.h>
52 #include <dev/ofw/ofw_bus.h>
53 #include <dev/ofw/ofw_bus_subr.h>
54 #endif
55
56 #include <dev/xdma/xdma.h>
57
58 #include <xdma_if.h>
59
60 /*
61  * Multiple xDMA controllers may work with single DMA device,
62  * so we have global lock for physical channel management.
63  */
64 static struct sx xdma_sx;
65
66 #define XDMA_LOCK()                     sx_xlock(&xdma_sx)
67 #define XDMA_UNLOCK()                   sx_xunlock(&xdma_sx)
68 #define XDMA_ASSERT_LOCKED()            sx_xassert(&xdma_sx, MA_OWNED)
69
70 /*
71  * Allocate virtual xDMA channel.
72  */
73 xdma_channel_t *
74 xdma_channel_alloc(xdma_controller_t *xdma, uint32_t caps)
75 {
76         xdma_channel_t *xchan;
77         int ret;
78
79         xchan = malloc(sizeof(xdma_channel_t), M_XDMA, M_WAITOK | M_ZERO);
80         xchan->xdma = xdma;
81         xchan->caps = caps;
82
83         XDMA_LOCK();
84
85         /* Request a real channel from hardware driver. */
86         ret = XDMA_CHANNEL_ALLOC(xdma->dma_dev, xchan);
87         if (ret != 0) {
88                 device_printf(xdma->dev,
89                     "%s: Can't request hardware channel.\n", __func__);
90                 XDMA_UNLOCK();
91                 free(xchan, M_XDMA);
92
93                 return (NULL);
94         }
95
96         TAILQ_INIT(&xchan->ie_handlers);
97
98         sx_init(&xchan->sx_lock, "xDMA chan");
99         sx_init(&xchan->sx_qin_lock, "xDMA qin");
100         sx_init(&xchan->sx_qout_lock, "xDMA qout");
101         sx_init(&xchan->sx_bank_lock, "xDMA bank");
102         sx_init(&xchan->sx_proc_lock, "xDMA proc");
103
104         TAILQ_INIT(&xchan->bank);
105         TAILQ_INIT(&xchan->queue_in);
106         TAILQ_INIT(&xchan->queue_out);
107         TAILQ_INIT(&xchan->processing);
108
109         TAILQ_INSERT_TAIL(&xdma->channels, xchan, xchan_next);
110
111         XDMA_UNLOCK();
112
113         return (xchan);
114 }
115
116 int
117 xdma_channel_free(xdma_channel_t *xchan)
118 {
119         xdma_controller_t *xdma;
120         int err;
121
122         xdma = xchan->xdma;
123         KASSERT(xdma != NULL, ("xdma is NULL"));
124
125         XDMA_LOCK();
126
127         /* Free the real DMA channel. */
128         err = XDMA_CHANNEL_FREE(xdma->dma_dev, xchan);
129         if (err != 0) {
130                 device_printf(xdma->dev,
131                     "%s: Can't free real hw channel.\n", __func__);
132                 XDMA_UNLOCK();
133                 return (-1);
134         }
135
136         if (xchan->flags & XCHAN_TYPE_SG)
137                 xdma_channel_free_sg(xchan);
138
139         xdma_teardown_all_intr(xchan);
140
141         sx_destroy(&xchan->sx_lock);
142         sx_destroy(&xchan->sx_qin_lock);
143         sx_destroy(&xchan->sx_qout_lock);
144         sx_destroy(&xchan->sx_bank_lock);
145         sx_destroy(&xchan->sx_proc_lock);
146
147         TAILQ_REMOVE(&xdma->channels, xchan, xchan_next);
148
149         free(xchan, M_XDMA);
150
151         XDMA_UNLOCK();
152
153         return (0);
154 }
155
156 int
157 xdma_setup_intr(xdma_channel_t *xchan,
158     int (*cb)(void *, xdma_transfer_status_t *),
159     void *arg, void **ihandler)
160 {
161         struct xdma_intr_handler *ih;
162         xdma_controller_t *xdma;
163
164         xdma = xchan->xdma;
165         KASSERT(xdma != NULL, ("xdma is NULL"));
166
167         /* Sanity check. */
168         if (cb == NULL) {
169                 device_printf(xdma->dev,
170                     "%s: Can't setup interrupt handler.\n",
171                     __func__);
172
173                 return (-1);
174         }
175
176         ih = malloc(sizeof(struct xdma_intr_handler),
177             M_XDMA, M_WAITOK | M_ZERO);
178         ih->cb = cb;
179         ih->cb_user = arg;
180
181         XCHAN_LOCK(xchan);
182         TAILQ_INSERT_TAIL(&xchan->ie_handlers, ih, ih_next);
183         XCHAN_UNLOCK(xchan);
184
185         if (ihandler != NULL)
186                 *ihandler = ih;
187
188         return (0);
189 }
190
191 int
192 xdma_teardown_intr(xdma_channel_t *xchan, struct xdma_intr_handler *ih)
193 {
194         xdma_controller_t *xdma;
195
196         xdma = xchan->xdma;
197         KASSERT(xdma != NULL, ("xdma is NULL"));
198
199         /* Sanity check. */
200         if (ih == NULL) {
201                 device_printf(xdma->dev,
202                     "%s: Can't teardown interrupt.\n", __func__);
203                 return (-1);
204         }
205
206         TAILQ_REMOVE(&xchan->ie_handlers, ih, ih_next);
207         free(ih, M_XDMA);
208
209         return (0);
210 }
211
212 int
213 xdma_teardown_all_intr(xdma_channel_t *xchan)
214 {
215         struct xdma_intr_handler *ih_tmp;
216         struct xdma_intr_handler *ih;
217         xdma_controller_t *xdma;
218
219         xdma = xchan->xdma;
220         KASSERT(xdma != NULL, ("xdma is NULL"));
221
222         TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp) {
223                 TAILQ_REMOVE(&xchan->ie_handlers, ih, ih_next);
224                 free(ih, M_XDMA);
225         }
226
227         return (0);
228 }
229
230 int
231 xdma_request(xdma_channel_t *xchan, struct xdma_request *req)
232 {
233         xdma_controller_t *xdma;
234         int ret;
235
236         xdma = xchan->xdma;
237
238         KASSERT(xdma != NULL, ("xdma is NULL"));
239
240         XCHAN_LOCK(xchan);
241         ret = XDMA_CHANNEL_REQUEST(xdma->dma_dev, xchan, req);
242         if (ret != 0) {
243                 device_printf(xdma->dev,
244                     "%s: Can't request a transfer.\n", __func__);
245                 XCHAN_UNLOCK(xchan);
246
247                 return (-1);
248         }
249         XCHAN_UNLOCK(xchan);
250
251         return (0);
252 }
253
254 int
255 xdma_control(xdma_channel_t *xchan, enum xdma_command cmd)
256 {
257         xdma_controller_t *xdma;
258         int ret;
259
260         xdma = xchan->xdma;
261         KASSERT(xdma != NULL, ("xdma is NULL"));
262
263         ret = XDMA_CHANNEL_CONTROL(xdma->dma_dev, xchan, cmd);
264         if (ret != 0) {
265                 device_printf(xdma->dev,
266                     "%s: Can't process command.\n", __func__);
267                 return (-1);
268         }
269
270         return (0);
271 }
272
273 void
274 xdma_callback(xdma_channel_t *xchan, xdma_transfer_status_t *status)
275 {
276         struct xdma_intr_handler *ih_tmp;
277         struct xdma_intr_handler *ih;
278         xdma_controller_t *xdma;
279
280         xdma = xchan->xdma;
281         KASSERT(xdma != NULL, ("xdma is NULL"));
282
283         TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp)
284                 if (ih->cb != NULL)
285                         ih->cb(ih->cb_user, status);
286
287         if (xchan->flags & XCHAN_TYPE_SG)
288                 xdma_queue_submit(xchan);
289 }
290
291 #ifdef FDT
292 /*
293  * Notify the DMA driver we have machine-dependent data in FDT.
294  */
295 static int
296 xdma_ofw_md_data(xdma_controller_t *xdma, pcell_t *cells, int ncells)
297 {
298         uint32_t ret;
299
300         ret = XDMA_OFW_MD_DATA(xdma->dma_dev,
301             cells, ncells, (void **)&xdma->data);
302
303         return (ret);
304 }
305
306 /*
307  * Allocate xdma controller.
308  */
309 xdma_controller_t *
310 xdma_ofw_get(device_t dev, const char *prop)
311 {
312         phandle_t node, parent;
313         xdma_controller_t *xdma;
314         device_t dma_dev;
315         pcell_t *cells;
316         int ncells;
317         int error;
318         int ndmas;
319         int idx;
320
321         node = ofw_bus_get_node(dev);
322         if (node <= 0)
323                 device_printf(dev,
324                     "%s called on not ofw based device.\n", __func__);
325
326         error = ofw_bus_parse_xref_list_get_length(node,
327             "dmas", "#dma-cells", &ndmas);
328         if (error) {
329                 device_printf(dev,
330                     "%s can't get dmas list.\n", __func__);
331                 return (NULL);
332         }
333
334         if (ndmas == 0) {
335                 device_printf(dev,
336                     "%s dmas list is empty.\n", __func__);
337                 return (NULL);
338         }
339
340         error = ofw_bus_find_string_index(node, "dma-names", prop, &idx);
341         if (error != 0) {
342                 device_printf(dev,
343                     "%s can't find string index.\n", __func__);
344                 return (NULL);
345         }
346
347         error = ofw_bus_parse_xref_list_alloc(node, "dmas", "#dma-cells",
348             idx, &parent, &ncells, &cells);
349         if (error != 0) {
350                 device_printf(dev,
351                     "%s can't get dma device xref.\n", __func__);
352                 return (NULL);
353         }
354
355         dma_dev = OF_device_from_xref(parent);
356         if (dma_dev == NULL) {
357                 device_printf(dev,
358                     "%s can't get dma device.\n", __func__);
359                 return (NULL);
360         }
361
362         xdma = malloc(sizeof(struct xdma_controller),
363             M_XDMA, M_WAITOK | M_ZERO);
364         xdma->dev = dev;
365         xdma->dma_dev = dma_dev;
366
367         TAILQ_INIT(&xdma->channels);
368
369         xdma_ofw_md_data(xdma, cells, ncells);
370         free(cells, M_OFWPROP);
371
372         return (xdma);
373 }
374 #endif
375
376 /*
377  * Free xDMA controller object.
378  */
379 int
380 xdma_put(xdma_controller_t *xdma)
381 {
382
383         XDMA_LOCK();
384
385         /* Ensure no channels allocated. */
386         if (!TAILQ_EMPTY(&xdma->channels)) {
387                 device_printf(xdma->dev, "%s: Can't free xDMA\n", __func__);
388                 return (-1);
389         }
390
391         free(xdma->data, M_DEVBUF);
392         free(xdma, M_XDMA);
393
394         XDMA_UNLOCK();
395
396         return (0);
397 }
398
399 static void
400 xdma_init(void)
401 {
402
403         sx_init(&xdma_sx, "xDMA");
404 }
405
406 SYSINIT(xdma, SI_SUB_DRIVERS, SI_ORDER_FIRST, xdma_init, NULL);