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 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 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 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 * 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 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