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