ata-dma.c revision 119404
110099Sjkh/*-
213884Sache * Copyright (c) 1998 - 2003 S�ren Schmidt <sos@FreeBSD.org>
310099Sjkh * All rights reserved.
413832Sache *
513884Sache * Redistribution and use in source and binary forms, with or without
613985Sache * modification, are permitted provided that the following conditions
713985Sache * are met:
813985Sache * 1. Redistributions of source code must retain the above copyright
913985Sache *    notice, this list of conditions and the following disclaimer,
1013985Sache *    without modification, immediately at the beginning of the file.
1113985Sache * 2. Redistributions in binary form must reproduce the above copyright
1213985Sache *    notice, this list of conditions and the following disclaimer in the
1313985Sache *    documentation and/or other materials provided with the distribution.
1413985Sache * 3. The name of the author may not be used to endorse or promote products
1513985Sache *    derived from this software without specific prior written permission.
1613985Sache *
1713985Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1813985Sache * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1910099Sjkh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2013985Sache * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2129103Scharnier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2229103Scharnier * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2350479Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2429103Scharnier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2529103Scharnier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2677168Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2777168Skris *
2877168Skris * $FreeBSD: head/sys/dev/ata/ata-dma.c 119404 2003-08-24 09:22:26Z sos $
2977168Skris */
3013985Sache
3129103Scharnier#include <sys/param.h>
3229103Scharnier#include <sys/systm.h>
3377168Skris#include <sys/ata.h>
3469793Sobrien#include <sys/kernel.h>
3510099Sjkh#include <sys/endian.h>
3610099Sjkh#include <sys/malloc.h>
3710099Sjkh#include <sys/lock.h>
3810099Sjkh#include <sys/mutex.h>
3977168Skris#include <sys/taskqueue.h>
4010099Sjkh#include <sys/bus.h>
4113884Sache#include <machine/bus.h>
4210099Sjkh#include <sys/rman.h>
4313985Sache#include <dev/pci/pcivar.h>
4413985Sache#include <dev/ata/ata-all.h>
4513985Sache#include <dev/ata/ata-pci.h>
4613985Sache
4713985Sache/* prototypes */
4813985Sachestatic void ata_dmasetupc_cb(void *, bus_dma_segment_t *, int, int);
4910099Sjkhstatic int ata_dmaalloc(struct ata_channel *);
5013985Sachestatic void ata_dmafree(struct ata_channel *);
5113985Sachestatic void ata_dmasetupd_cb(void *, bus_dma_segment_t *, int, int);
5213985Sachestatic int ata_dmasetup(struct ata_device *, caddr_t, int32_t);
5313985Sache
5413985Sache/* local vars */
5513985Sachestatic MALLOC_DEFINE(M_ATADMA, "ATA DMA", "ATA driver DMA");
5613985Sache
5713985Sache/* misc defines */
5813985Sache#define MAXSEGSZ	PAGE_SIZE
5913985Sache#define MAXTABSZ	PAGE_SIZE
6013985Sache#define MAXCTLDMASZ	(2 * (MAXTABSZ + MAXPHYS))
6113985Sache
6213985Sachestruct ata_dc_cb_args {
6313985Sache    bus_addr_t maddr;
6413985Sache    int error;
6513985Sache};
6613985Sache
6713985Sacheint
6813985Sacheata_dmainit(struct ata_channel *ch)
6913985Sache{
7013985Sache    if (!(ch->dma =
7113985Sache	malloc(sizeof(struct ata_dma), M_ATADMA, M_NOWAIT | M_ZERO)))
7263091Sjoe	return ENOMEM;
7377168Skris    ch->dma->alloc = ata_dmaalloc;
7477168Skris    ch->dma->free = ata_dmafree;
7532782Sjmz    ch->dma->setup = ata_dmasetup;
7632782Sjmz    ch->dma->start = ata_dmastart;
7732782Sjmz    ch->dma->stop = ata_dmastop;
7813985Sache    ch->dma->alignment = 2;
7910099Sjkh    ch->dma->max_iosize = 64*1024;
8010099Sjkh    return 0;
8187568Smikeh}
8287568Smikeh
8387568Smikeh
8410099Sjkhstatic void
8513985Sacheata_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
8613985Sache{
8713985Sache    struct ata_dc_cb_args *cba = (struct ata_dc_cb_args *)xsc;
8813985Sache
8913985Sache    if (!(cba->error = error))
9013985Sache	cba->maddr = segs[0].ds_addr;
9177168Skris}
9213985Sache
9313985Sachestatic int
9413985Sacheata_dmaalloc(struct ata_channel *ch)
9513985Sache{
9613985Sache    struct ata_dc_cb_args ccba;
9777168Skris    int error;
9813985Sache
9913985Sache    if (!ch->dma->dmatag) {
10013985Sache	if (bus_dma_tag_create(NULL, 1, 0,
10113985Sache			       BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
10232782Sjmz			       NULL, NULL, MAXCTLDMASZ, ATA_DMA_ENTRIES,
10313985Sache			       BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL,
10413985Sache			       &ch->dma->dmatag)) {
10563091Sjoe	    ata_printf(ch, -1,
10687568Smikeh		       "WARNING - DMA tag allocation failed, disabling DMA\n");
10710099Sjkh	}
10810099Sjkh    }
10913985Sache    if (!ch->dma->cdmatag) {
11010099Sjkh	if ((error = bus_dma_tag_create(ch->dma->dmatag, 1, PAGE_SIZE,
11113985Sache					BUS_SPACE_MAXADDR_32BIT,
11213985Sache					BUS_SPACE_MAXADDR, NULL, NULL,
11313985Sache					MAXTABSZ, 1, MAXTABSZ,
11413985Sache					BUS_DMA_ALLOCNOW, NULL, NULL,
11510099Sjkh					&ch->dma->cdmatag)))
11613985Sache	    return error;
11713985Sache    }
11813985Sache    if (!ch->dma->ddmatag) {
11913985Sache	if ((error = bus_dma_tag_create(ch->dma->dmatag, ch->dma->alignment, 0,
12013985Sache					BUS_SPACE_MAXADDR_32BIT,
12113985Sache					BUS_SPACE_MAXADDR, NULL, NULL,
12213985Sache					MAXPHYS, ATA_DMA_ENTRIES, MAXSEGSZ,
12377168Skris					BUS_DMA_ALLOCNOW, NULL, NULL,
12413985Sache					&ch->dma->ddmatag)))
12513985Sache	    return error;
12663091Sjoe    }
12713985Sache    if (!ch->dma->mdmatab) {
12813985Sache	if ((error = bus_dmamem_alloc(ch->dma->cdmatag,
12913985Sache				      (void **)&ch->dma->dmatab, 0,
13013985Sache				      &ch->dma->cdmamap)))
13113985Sache	    return error;
13213985Sache
13313985Sache	if ((error = bus_dmamap_load(ch->dma->cdmatag, ch->dma->cdmamap,
13413985Sache				     ch->dma->dmatab, MAXTABSZ,
13513985Sache				     ata_dmasetupc_cb, &ccba, 0)) != 0 ||
13687568Smikeh	    ccba.error != 0) {
13787568Smikeh	    bus_dmamem_free(ch->dma->cdmatag, ch->dma->dmatab,ch->dma->cdmamap);
13887568Smikeh	    return error;
13987568Smikeh	}
14087568Smikeh	ch->dma->mdmatab = ccba.maddr;
14187568Smikeh    }
14210099Sjkh    if (!ch->dma->ddmamap) {
14310099Sjkh	if ((error = bus_dmamap_create(ch->dma->ddmatag, 0,
14410099Sjkh				       &ch->dma->ddmamap)) != 0)
14510099Sjkh	    return error;
14687568Smikeh    }
14787568Smikeh    return 0;
14813985Sache}
14910099Sjkh
15010099Sjkhstatic void
15110099Sjkhata_dmafree(struct ata_channel *ch)
15210099Sjkh{
15313985Sache    if (ch->dma->mdmatab) {
15413985Sache	bus_dmamap_unload(ch->dma->cdmatag, ch->dma->cdmamap);
15513985Sache	bus_dmamem_free(ch->dma->cdmatag, ch->dma->dmatab, ch->dma->cdmamap);
15613985Sache	ch->dma->mdmatab = 0;
15713985Sache	ch->dma->cdmamap = NULL;
15813985Sache	ch->dma->dmatab = NULL;
15913985Sache    }
16013985Sache    if (ch->dma->ddmamap) {
16110099Sjkh	bus_dmamap_destroy(ch->dma->ddmatag, ch->dma->ddmamap);
16210099Sjkh	ch->dma->ddmamap = NULL;
16310099Sjkh    }
16410099Sjkh    if (ch->dma->cdmatag) {
16513985Sache	bus_dma_tag_destroy(ch->dma->cdmatag);
16613985Sache	ch->dma->cdmatag = NULL;
16710099Sjkh    }
16810099Sjkh    if (ch->dma->ddmatag) {
16910099Sjkh	bus_dma_tag_destroy(ch->dma->ddmatag);
17010099Sjkh	ch->dma->ddmatag = NULL;
17143479Sbillf    }
17210099Sjkh    if (ch->dma->dmatag) {
17310099Sjkh	bus_dma_tag_destroy(ch->dma->dmatag);
17410099Sjkh	ch->dma->dmatag = NULL;
17587568Smikeh    }
17671122Sjoe}
17771122Sjoe
17871122Sjoestruct ata_dmasetup_data_cb_args {
17971122Sjoe    struct ata_dmaentry *dmatab;
18071122Sjoe    int error;
18171122Sjoe};
18271122Sjoe
18371122Sjoestatic void
18471122Sjoeata_dmasetupd_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
18571122Sjoe{
18671122Sjoe    struct ata_dmasetup_data_cb_args *cba =
18710099Sjkh	(struct ata_dmasetup_data_cb_args *)xsc;
18810099Sjkh    bus_size_t cnt;
18910099Sjkh    u_int32_t lastcount;
19010099Sjkh    int i, j;
19110099Sjkh
19210099Sjkh    cba->error = error;
19310099Sjkh    if (error != 0)
19410099Sjkh	return;
19510099Sjkh    lastcount = j = 0;
19610099Sjkh    for (i = 0; i < nsegs; i++) {
19710099Sjkh	/*
19810099Sjkh	 * A maximum segment size was specified for bus_dma_tag_create, but
19910099Sjkh	 * some busdma code does not seem to honor this, so fix up if needed.
20010099Sjkh	 */
20110099Sjkh	for (cnt = 0; cnt < segs[i].ds_len; cnt += MAXSEGSZ, j++) {
20210099Sjkh	    cba->dmatab[j].base = htole32(segs[i].ds_addr + cnt);
20310099Sjkh	    lastcount = ulmin(segs[i].ds_len - cnt, MAXSEGSZ) & 0xffff;
20410099Sjkh	    cba->dmatab[j].count = htole32(lastcount);
20510099Sjkh	}
20610099Sjkh    }
20710099Sjkh    cba->dmatab[j - 1].count = htole32(lastcount | ATA_DMA_EOT);
20810099Sjkh}
20910099Sjkh
21010099Sjkhstatic int
21110099Sjkhata_dmasetup(struct ata_device *atadev, caddr_t data, int32_t count)
21210099Sjkh{
21310099Sjkh    struct ata_channel *ch = atadev->channel;
21413985Sache
21510099Sjkh    if (((uintptr_t)data & (ch->dma->alignment - 1)) ||
21610099Sjkh	(count & (ch->dma->alignment - 1))) {
21710099Sjkh	ata_prtdev(atadev, "FAILURE - non aligned DMA transfer attempted\n");
21870149Sdes	return -1;
21970149Sdes    }
22070149Sdes    if (!count) {
22175324Sjoe	ata_prtdev(atadev, "FAILURE - zero length DMA transfer attempted\n");
22275324Sjoe	return -1;
22375324Sjoe    }
22475324Sjoe    if (count > ch->dma->max_iosize) {
22575324Sjoe	ata_prtdev(atadev,
22675324Sjoe		   "FAILURE - oversized DMA transfer attempted %d > %d\n",
22775324Sjoe		   count, ch->dma->max_iosize);
22875324Sjoe	return -1;
22975324Sjoe    }
23070149Sdes    return 0;
23113985Sache}
23229103Scharnier
23310099Sjkhint
23410099Sjkhata_dmastart(struct ata_channel *ch, caddr_t data, int32_t count, int dir)
23510099Sjkh{
23610099Sjkh    struct ata_dmasetup_data_cb_args cba;
23710099Sjkh
23810099Sjkh    if (ch->dma->flags & ATA_DMA_ACTIVE)
23913985Sache	    panic("ata_dmasetup: transfer active on this device!");
24010099Sjkh
24113985Sache    cba.dmatab = ch->dma->dmatab;
24210099Sjkh    bus_dmamap_sync(ch->dma->cdmatag, ch->dma->cdmamap, BUS_DMASYNC_PREWRITE);
24310099Sjkh    if (bus_dmamap_load(ch->dma->ddmatag, ch->dma->ddmamap, data, count,
24413985Sache			ata_dmasetupd_cb, &cba, 0) || cba.error)
24510099Sjkh	return -1;
24610099Sjkh
24713985Sache    bus_dmamap_sync(ch->dma->ddmatag, ch->dma->ddmamap,
24810099Sjkh		    dir ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
24910099Sjkh
25010099Sjkh    ch->dma->flags = dir ? (ATA_DMA_ACTIVE | ATA_DMA_READ) : ATA_DMA_ACTIVE;
25110099Sjkh    return 0;
25210099Sjkh}
25313985Sache
25410099Sjkhint
25510099Sjkhata_dmastop(struct ata_channel *ch)
25610099Sjkh{
25710099Sjkh    bus_dmamap_sync(ch->dma->cdmatag, ch->dma->cdmamap, BUS_DMASYNC_POSTWRITE);
25813985Sache
25910099Sjkh    bus_dmamap_sync(ch->dma->ddmatag, ch->dma->ddmamap,
26013985Sache		    (ch->dma->flags & ATA_DMA_READ) != 0 ?
26110099Sjkh		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
26210099Sjkh    bus_dmamap_unload(ch->dma->ddmatag, ch->dma->ddmamap);
26310099Sjkh
26410099Sjkh    ch->dma->flags = 0;
26510099Sjkh    return 0;
26610099Sjkh}
26710099Sjkh