xref: /freebsd/sys/dev/xilinx/axidma.c (revision 974ecf68903d3887324f534a3bca21f1ae501819)
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