sfxge_dma.c revision 280581
118334Speter/*- 290075Sobrien * Copyright (c) 2010-2011 Solarflare Communications, Inc. 3117395Skan * All rights reserved. 418334Speter * 518334Speter * This software was developed in part by Philip Paeps under contract for 618334Speter * Solarflare Communications, Inc. 718334Speter * 818334Speter * Redistribution and use in source and binary forms, with or without 918334Speter * modification, are permitted provided that the following conditions 1018334Speter * are met: 1118334Speter * 1. Redistributions of source code must retain the above copyright 1218334Speter * notice, this list of conditions and the following disclaimer. 1318334Speter * 2. Redistributions in binary form must reproduce the above copyright 1418334Speter * notice, this list of conditions and the following disclaimer in the 1518334Speter * documentation and/or other materials provided with the distribution. 1618334Speter * 1718334Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1818334Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1918334Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2018334Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2118334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2218334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2318334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2418334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2518334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2618334Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2718334Speter * SUCH DAMAGE. 2818334Speter */ 2918334Speter 3018334Speter#include <sys/cdefs.h> 3118334Speter__FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/sfxge_dma.c 280581 2015-03-25 13:03:36Z arybchik $"); 3250397Sobrien 3318334Speter#include <sys/param.h> 3418334Speter#include <sys/bus.h> 3590075Sobrien 3618334Speter#include <machine/bus.h> 3718334Speter 3818334Speter#include "common/efx.h" 3918334Speter 4018334Speter#include "sfxge.h" 4150397Sobrien 4250397Sobrienstatic void 4390075Sobriensfxge_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 4490075Sobrien{ 4550397Sobrien bus_addr_t *addr; 4690075Sobrien 47117395Skan addr = arg; 48117395Skan 4990075Sobrien if (error != 0) { 5018334Speter *addr = 0; 5152284Sobrien return; 5252284Sobrien } 5352284Sobrien 54117395Skan *addr = segs[0].ds_addr; 5552284Sobrien} 5652284Sobrien 57117395Skanint 5852284Sobriensfxge_dma_map_sg_collapse(bus_dma_tag_t tag, bus_dmamap_t map, 5952284Sobrien struct mbuf **mp, bus_dma_segment_t *segs, int *nsegs, int maxsegs) 6052284Sobrien{ 6152284Sobrien bus_dma_segment_t *psegs; 6290075Sobrien struct mbuf *m; 6390075Sobrien int seg_count; 64117395Skan int defragged; 6590075Sobrien int err; 66117395Skan 6790075Sobrien m = *mp; 6890075Sobrien defragged = err = seg_count = 0; 6990075Sobrien 7090075Sobrien KASSERT(m->m_pkthdr.len, ("packet has zero header length")); 7190075Sobrien 7290075Sobrienretry: 7390075Sobrien psegs = segs; 7490075Sobrien seg_count = 0; 7590075Sobrien if (m->m_next == NULL) { 7652284Sobrien sfxge_map_mbuf_fast(tag, map, m, segs); 7790075Sobrien *nsegs = 1; 7890075Sobrien return (0); 7990075Sobrien } 8090075Sobrien#if defined(__i386__) || defined(__amd64__) 8190075Sobrien while (m != NULL && seg_count < maxsegs) { 8290075Sobrien /* 8390075Sobrien * firmware doesn't like empty segments 8490075Sobrien */ 8552284Sobrien if (m->m_len != 0) { 8690075Sobrien seg_count++; 87117395Skan sfxge_map_mbuf_fast(tag, map, m, psegs); 8890075Sobrien psegs++; 8990075Sobrien } 9090075Sobrien m = m->m_next; 9150397Sobrien } 9218334Speter#else 9318334Speter err = bus_dmamap_load_mbuf_sg(tag, map, *mp, segs, &seg_count, 0); 9490075Sobrien#endif 95117395Skan if (seg_count == 0) { 9690075Sobrien err = EFBIG; 9790075Sobrien goto err_out; 9818334Speter } else if (err == EFBIG || seg_count >= maxsegs) { 9918334Speter if (!defragged) { 10050397Sobrien m = m_defrag(*mp, M_NOWAIT); 101117395Skan if (m == NULL) { 10290075Sobrien err = ENOBUFS; 10390075Sobrien goto err_out; 10418334Speter } 10518334Speter *mp = m; 10618334Speter defragged = 1; 10718334Speter goto retry; 10818334Speter } 10950397Sobrien err = EFBIG; 11050397Sobrien goto err_out; 11150397Sobrien } 11250397Sobrien *nsegs = seg_count; 11318334Speter 11418334Spetererr_out: 11590075Sobrien return (err); 11690075Sobrien} 11750397Sobrien 118117395Skanvoid 11950397Sobriensfxge_dma_free(efsys_mem_t *esmp) 12050397Sobrien{ 12118334Speter 12218334Speter bus_dmamap_unload(esmp->esm_tag, esmp->esm_map); 12318334Speter bus_dmamem_free(esmp->esm_tag, esmp->esm_base, esmp->esm_map); 12490075Sobrien bus_dma_tag_destroy(esmp->esm_tag); 12590075Sobrien 12690075Sobrien esmp->esm_addr = 0; 12750397Sobrien esmp->esm_base = NULL; 12890075Sobrien} 12918334Speter 13018334Speterint 13118334Spetersfxge_dma_alloc(struct sfxge_softc *sc, bus_size_t len, efsys_mem_t *esmp) 13218334Speter{ 13318334Speter void *vaddr; 13452284Sobrien 13552284Sobrien /* Create the child DMA tag. */ 13690075Sobrien if (bus_dma_tag_create(sc->parent_dma_tag, PAGE_SIZE, 0, 13718334Speter MIN(0x3FFFFFFFFFFFUL, BUS_SPACE_MAXADDR), BUS_SPACE_MAXADDR, NULL, 13818334Speter NULL, len, 1, len, 0, NULL, NULL, &esmp->esm_tag) != 0) { 13918334Speter device_printf(sc->dev, "Couldn't allocate txq DMA tag\n"); 14052284Sobrien return (ENOMEM); 14152284Sobrien } 14290075Sobrien 14352284Sobrien /* Allocate kernel memory. */ 14490075Sobrien if (bus_dmamem_alloc(esmp->esm_tag, (void **)&vaddr, 14590075Sobrien BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, 14618334Speter &esmp->esm_map) != 0) { 14752284Sobrien device_printf(sc->dev, "Couldn't allocate DMA memory\n"); 14818334Speter bus_dma_tag_destroy(esmp->esm_tag); 14952284Sobrien return (ENOMEM); 15018334Speter } 15152284Sobrien 15252284Sobrien /* Load map into device memory. */ 15390075Sobrien if (bus_dmamap_load(esmp->esm_tag, esmp->esm_map, vaddr, len, 15452284Sobrien sfxge_dma_cb, &esmp->esm_addr, 0) != 0) { 15552284Sobrien device_printf(sc->dev, "Couldn't load DMA mapping\n"); 15652284Sobrien bus_dmamem_free(esmp->esm_tag, vaddr, esmp->esm_map); 15752284Sobrien bus_dma_tag_destroy(esmp->esm_tag); 15818334Speter return (ENOMEM); 15918334Speter } 16018334Speter 16118334Speter /* 16218334Speter * The callback gets error information about the mapping 16318334Speter * and will have set esm_addr to 0 if something went 16418334Speter * wrong. 16518334Speter */ 16690075Sobrien if (esmp->esm_addr == 0) { 16718334Speter bus_dmamem_free(esmp->esm_tag, vaddr, esmp->esm_map); 16818334Speter bus_dma_tag_destroy(esmp->esm_tag); 16918334Speter return (ENOMEM); 17018334Speter } 17118334Speter 17250397Sobrien esmp->esm_base = vaddr; 17318334Speter 17418334Speter return (0); 17518334Speter} 17618334Speter 17718334Spetervoid 17818334Spetersfxge_dma_fini(struct sfxge_softc *sc) 17918334Speter{ 18018334Speter 18118334Speter bus_dma_tag_destroy(sc->parent_dma_tag); 18218334Speter} 18390075Sobrien 18418334Speterint 18590075Sobriensfxge_dma_init(struct sfxge_softc *sc) 18618334Speter{ 18718334Speter 18818334Speter /* Create the parent dma tag. */ 18990075Sobrien if (bus_dma_tag_create(bus_get_dma_tag(sc->dev), /* parent */ 19090075Sobrien 1, 0, /* algnmnt, boundary */ 19118334Speter BUS_SPACE_MAXADDR, /* lowaddr */ 19290075Sobrien BUS_SPACE_MAXADDR, /* highaddr */ 19318334Speter NULL, NULL, /* filter, filterarg */ 19418334Speter BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 19518334Speter BUS_SPACE_UNRESTRICTED, /* nsegments */ 19618334Speter BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 19718334Speter 0, /* flags */ 19818334Speter NULL, NULL, /* lock, lockarg */ 19918334Speter &sc->parent_dma_tag) != 0) { 20018334Speter device_printf(sc->dev, "Cannot allocate parent DMA tag\n"); 20118334Speter return (ENOMEM); 20218334Speter } 20350397Sobrien 20452284Sobrien return (0); 20552284Sobrien} 20652284Sobrien