1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019-2025 Ruslan Bukin <br@bsdpad.com>
5 *
6 * This software was developed by SRI International and the University of
7 * Cambridge Computer Laboratory (Department of Computer Science and
8 * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
9 * DARPA SSITH research programme.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * Xilinx AXI Ethernet DMA controller driver.
35 */
36
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/rman.h>
42
43 #include <machine/bus.h>
44
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
47
48 #include <dev/xilinx/axidma.h>
49
50 #include "axidma_if.h"
51
52 #define AXIDMA_RD4(_sc, _reg) \
53 bus_space_read_4(_sc->bst, _sc->bsh, _reg)
54 #define AXIDMA_WR4(_sc, _reg, _val) \
55 bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)
56 #define AXIDMA_RD8(_sc, _reg) \
57 bus_space_read_8(_sc->bst, _sc->bsh, _reg)
58 #define AXIDMA_WR8(_sc, _reg, _val) \
59 bus_space_write_8(_sc->bst, _sc->bsh, _reg, _val)
60
61 #define dprintf(fmt, ...)
62
63 #define AXIDMA_MAX_CHANNELS 2
64
65 struct axidma_softc {
66 device_t dev;
67 struct resource *res[1 + AXIDMA_MAX_CHANNELS];
68 bus_space_tag_t bst;
69 bus_space_handle_t bsh;
70 void *ih[AXIDMA_MAX_CHANNELS];
71 };
72
73 static struct resource_spec axidma_spec[] = {
74 { SYS_RES_MEMORY, 0, RF_ACTIVE },
75 { SYS_RES_IRQ, 0, RF_ACTIVE },
76 { SYS_RES_IRQ, 1, RF_ACTIVE },
77 { -1, 0 }
78 };
79
80 static struct ofw_compat_data compat_data[] = {
81 { "xlnx,eth-dma", 1 },
82 { NULL, 0 },
83 };
84
85 static int
axidma_probe(device_t dev)86 axidma_probe(device_t dev)
87 {
88
89 if (!ofw_bus_status_okay(dev))
90 return (ENXIO);
91
92 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
93 return (ENXIO);
94
95 device_set_desc(dev, "Xilinx AXI DMA");
96
97 return (BUS_PROBE_DEFAULT);
98 }
99
100 static int
axidma_attach(device_t dev)101 axidma_attach(device_t dev)
102 {
103 struct axidma_softc *sc;
104 phandle_t xref, node;
105
106 sc = device_get_softc(dev);
107 sc->dev = dev;
108
109 if (bus_alloc_resources(dev, axidma_spec, sc->res)) {
110 device_printf(dev, "could not allocate resources.\n");
111 return (ENXIO);
112 }
113
114 /* CSR memory interface */
115 sc->bst = rman_get_bustag(sc->res[0]);
116 sc->bsh = rman_get_bushandle(sc->res[0]);
117
118 node = ofw_bus_get_node(dev);
119 xref = OF_xref_from_node(node);
120 OF_device_register_xref(xref, dev);
121
122 return (0);
123 }
124
125 static int
axidma_reset(device_t dev,int chan_id)126 axidma_reset(device_t dev, int chan_id)
127 {
128 struct axidma_softc *sc;
129 int timeout;
130
131 sc = device_get_softc(dev);
132
133 AXIDMA_WR4(sc, AXI_DMACR(chan_id), DMACR_RESET);
134
135 timeout = 100;
136
137 do {
138 if ((AXIDMA_RD4(sc, AXI_DMACR(chan_id)) & DMACR_RESET) == 0)
139 break;
140 } while (timeout--);
141
142 dprintf("timeout %d\n", timeout);
143
144 if (timeout == 0)
145 return (-1);
146
147 dprintf("%s: read control after reset: %x\n",
148 __func__, AXIDMA_RD4(sc, AXI_DMACR(chan_id)));
149
150 return (0);
151 }
152
153 static struct resource *
axidma_memres(device_t dev)154 axidma_memres(device_t dev)
155 {
156 struct axidma_softc *sc;
157
158 sc = device_get_softc(dev);
159
160 return (sc->res[0]);
161 }
162
163 static int
axidma_setup_cb(device_t dev,int chan_id,void (* cb)(void *),void * arg)164 axidma_setup_cb(device_t dev, int chan_id, void (*cb)(void *), void *arg)
165 {
166 struct axidma_softc *sc;
167 int error;
168
169 sc = device_get_softc(dev);
170
171 if (sc->ih[chan_id] != NULL)
172 return (EEXIST);
173
174 error = bus_setup_intr(dev, sc->res[chan_id + 1],
175 INTR_TYPE_MISC | INTR_MPSAFE, NULL, cb, arg,
176 &sc->ih[chan_id]);
177 if (error)
178 device_printf(dev, "Unable to alloc interrupt resource.\n");
179
180 return (error);
181 }
182
183 static device_method_t axidma_methods[] = {
184 /* Device interface */
185 DEVMETHOD(device_probe, axidma_probe),
186 DEVMETHOD(device_attach, axidma_attach),
187
188 /* Axidma interface */
189 DEVMETHOD(axidma_reset, axidma_reset),
190 DEVMETHOD(axidma_memres, axidma_memres),
191 DEVMETHOD(axidma_setup_cb, axidma_setup_cb),
192
193 DEVMETHOD_END
194 };
195
196 static driver_t axidma_driver = {
197 "axidma",
198 axidma_methods,
199 sizeof(struct axidma_softc),
200 };
201
202 EARLY_DRIVER_MODULE(axidma, simplebus, axidma_driver, 0, 0,
203 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
204