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