ata-dma.c revision 111188
145095Ssos/*-
2111188Ssos * Copyright (c) 1998 - 2003 S�ren Schmidt <sos@FreeBSD.org>
345095Ssos * All rights reserved.
445095Ssos *
545095Ssos * Redistribution and use in source and binary forms, with or without
645095Ssos * modification, are permitted provided that the following conditions
745095Ssos * are met:
845095Ssos * 1. Redistributions of source code must retain the above copyright
945095Ssos *    notice, this list of conditions and the following disclaimer,
1045095Ssos *    without modification, immediately at the beginning of the file.
1145095Ssos * 2. Redistributions in binary form must reproduce the above copyright
1245095Ssos *    notice, this list of conditions and the following disclaimer in the
1345095Ssos *    documentation and/or other materials provided with the distribution.
1445095Ssos * 3. The name of the author may not be used to endorse or promote products
1545095Ssos *    derived from this software without specific prior written permission.
1645095Ssos *
1745095Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1845095Ssos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1945095Ssos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2045095Ssos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2145095Ssos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2245095Ssos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2345095Ssos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2445095Ssos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2545095Ssos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2645095Ssos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2745095Ssos *
2850477Speter * $FreeBSD: head/sys/dev/ata/ata-dma.c 111188 2003-02-20 20:02:32Z sos $
2945095Ssos */
3045095Ssos
3145095Ssos#include <sys/param.h>
3245095Ssos#include <sys/systm.h>
3374302Ssos#include <sys/ata.h>
34111188Ssos#include <sys/kernel.h>
3595533Smike#include <sys/endian.h>
3645095Ssos#include <sys/malloc.h>
3745798Ssos#include <sys/bus.h>
3845095Ssos#include <pci/pcivar.h>
3966106Ssos#include <machine/bus.h>
4072106Ssos#include <sys/rman.h>
4145095Ssos#include <dev/ata/ata-all.h>
42111188Ssos#include <dev/ata/ata-pci.h>
4345095Ssos
4452067Ssos/* prototypes */
45111188Ssosstatic void ata_dmasetupc_cb(void *, bus_dma_segment_t *, int, int);
46111188Ssosstatic int ata_dmaalloc(struct ata_device *);
47111188Ssosstatic void ata_dmafree(struct ata_device *);
48111188Ssosstatic void ata_dmacreate(struct ata_channel *);
49111188Ssosstatic void ata_dmadestroy(struct ata_channel *);
5093882Ssosstatic void ata_dmasetupd_cb(void *, bus_dma_segment_t *, int, int);
51111188Ssosstatic int ata_dmasetup(struct ata_device *, caddr_t, int32_t);
52111188Ssos/*
53111188Ssosstatic int ata_dmastart(struct ata_device *, caddr_t, int32_t, int);
54111188Ssosstatic int ata_dmastop(struct ata_device *);
55111188Ssos*/
56111188Ssosstatic int ata_dmastatus(struct ata_channel *);
5752067Ssos
58111188Ssos/* local vars */
59111188Ssosstatic MALLOC_DEFINE(M_ATADMA, "ATA DMA", "ATA driver DMA");
60111188Ssos
6152067Ssos/* misc defines */
62111188Ssos#define MAXSEGSZ	PAGE_SIZE
63111188Ssos#define MAXTABSZ	PAGE_SIZE
64111188Ssos#define MAXCTLDMASZ	(2 * (MAXTABSZ + MAXPHYS))
6545720Speter
6693882Ssosstruct ata_dc_cb_args {
6793882Ssos    bus_addr_t maddr;
6893882Ssos    int error;
6993882Ssos};
7093882Ssos
71111188Ssosint
72111188Ssosata_dmainit(struct ata_channel *ch)
73111188Ssos{
74111188Ssos    if (!(ch->dma =
75111188Ssos	malloc(sizeof(struct ata_dma_funcs), M_ATADMA, M_NOWAIT | M_ZERO)))
76111188Ssos	return ENOMEM;
77111188Ssos    ch->dma->create = ata_dmacreate;
78111188Ssos    ch->dma->destroy = ata_dmadestroy;
79111188Ssos    ch->dma->alloc = ata_dmaalloc;
80111188Ssos    ch->dma->free = ata_dmafree;
81111188Ssos    ch->dma->setup = ata_dmasetup;
82111188Ssos    ch->dma->start = ata_dmastart;
83111188Ssos    ch->dma->stop = ata_dmastop;
84111188Ssos    ch->dma->status = ata_dmastatus;
85111188Ssos    ch->dma->alignment = 2;
86111188Ssos    return 0;
87111188Ssos}
88111188Ssos
89111188Ssos
9093882Ssosstatic void
9193882Ssosata_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
9266070Ssos{
9393882Ssos    struct ata_dc_cb_args *cba = (struct ata_dc_cb_args *)xsc;
9466070Ssos
9593882Ssos    if (!(cba->error = error))
9693882Ssos	cba->maddr = segs[0].ds_addr;
9793882Ssos}
9893882Ssos
99111188Ssosstatic int
10093882Ssosata_dmaalloc(struct ata_device *atadev)
10193882Ssos{
10293882Ssos    struct ata_channel *ch;
10393882Ssos    struct ata_dc_cb_args ccba;
10493882Ssos    struct ata_dmastate *ds;
10593882Ssos    int error;
10693882Ssos
10793882Ssos    ch = atadev->channel;
10893882Ssos    ds = &atadev->dmastate;
10993882Ssos    if (!ds->cdmatag) {
11093882Ssos	if ((error = bus_dma_tag_create(ch->dmatag, 1, PAGE_SIZE,
11193882Ssos					BUS_SPACE_MAXADDR_32BIT,
11293882Ssos					BUS_SPACE_MAXADDR, NULL, NULL,
11393882Ssos					MAXTABSZ, 1, MAXTABSZ,
11493882Ssos					BUS_DMA_ALLOCNOW, &ds->cdmatag)))
11593882Ssos	    return error;
11693882Ssos    }
11793882Ssos    if (!ds->ddmatag) {
118111188Ssos	if ((error = bus_dma_tag_create(ch->dmatag, ch->dma->alignment, 0,
11993882Ssos					BUS_SPACE_MAXADDR_32BIT,
12093882Ssos					BUS_SPACE_MAXADDR, NULL, NULL,
12193882Ssos					MAXPHYS, ATA_DMA_ENTRIES, MAXSEGSZ,
12293882Ssos					BUS_DMA_ALLOCNOW, &ds->ddmatag)))
12393882Ssos	    return error;
12493882Ssos    }
12593882Ssos    if (!ds->mdmatab) {
12693882Ssos	if ((error = bus_dmamem_alloc(ds->cdmatag, (void **)&ds->dmatab, 0,
12793882Ssos				      &ds->cdmamap)))
12893882Ssos	    return error;
12993882Ssos
13093882Ssos	if ((error = bus_dmamap_load(ds->cdmatag, ds->cdmamap, ds->dmatab,
13193882Ssos				     MAXTABSZ, ata_dmasetupc_cb, &ccba,
13293882Ssos				     0)) != 0 || ccba.error != 0) {
13393882Ssos	    bus_dmamem_free(ds->cdmatag, ds->dmatab, ds->cdmamap);
13493882Ssos	    return error;
13566070Ssos	}
13693882Ssos	ds->mdmatab = ccba.maddr;
13766070Ssos    }
13893882Ssos    if (!ds->ddmamap) {
13993882Ssos	if ((error = bus_dmamap_create(ds->ddmatag, 0, &ds->ddmamap)) != 0)
14093882Ssos	    return error;
14193882Ssos    }
14293882Ssos    return 0;
14366070Ssos}
14466070Ssos
145111188Ssosstatic void
14693882Ssosata_dmafree(struct ata_device *atadev)
14745095Ssos{
14893882Ssos    struct ata_dmastate *ds;
14993882Ssos
15093882Ssos    ds = &atadev->dmastate;
15193882Ssos    if (ds->mdmatab) {
15293882Ssos	bus_dmamap_unload(ds->cdmatag, ds->cdmamap);
15393882Ssos	bus_dmamem_free(ds->cdmatag, ds->dmatab, ds->cdmamap);
15493882Ssos	ds->mdmatab = 0;
15593882Ssos	ds->cdmamap = NULL;
15693882Ssos	ds->dmatab = NULL;
15793882Ssos    }
15893882Ssos    if (ds->ddmamap) {
15993882Ssos	bus_dmamap_destroy(ds->ddmatag, ds->ddmamap);
16093882Ssos	ds->ddmamap = NULL;
16193882Ssos    }
16293882Ssos    if (ds->cdmatag) {
16393882Ssos	bus_dma_tag_destroy(ds->cdmatag);
16493882Ssos	ds->cdmatag = NULL;
16593882Ssos    }
16693882Ssos    if (ds->ddmatag) {
16793882Ssos	bus_dma_tag_destroy(ds->ddmatag);
16893882Ssos	ds->ddmatag = NULL;
16993882Ssos    }
17093882Ssos}
17193882Ssos
17293882Ssosstatic void
173111188Ssosata_dmacreate(struct ata_channel *ch)
17493882Ssos{
17593882Ssos
176111188Ssos    if (!ch->dmatag) {
17793882Ssos	if (bus_dma_tag_create(NULL, 1, 0,
17893882Ssos			       BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
17993882Ssos			       NULL, NULL, MAXCTLDMASZ, ATA_DMA_ENTRIES,
180111188Ssos			       BUS_SPACE_MAXSIZE_32BIT, 0, &ch->dmatag)) {
181111188Ssos	    printf("DMA tag allocation failed, disabling DMA\n");
18293882Ssos	}
18393882Ssos    }
18493882Ssos}
18593882Ssos
186111188Ssosstatic void
187111188Ssosata_dmadestroy(struct ata_channel *ch)
18893882Ssos{
18945095Ssos
190111188Ssos    if (ch->dmatag) {
191111188Ssos	bus_dma_tag_destroy(ch->dmatag);
192111188Ssos	ch->dmatag = NULL;
19352067Ssos    }
19445095Ssos}
19545095Ssos
19693882Ssosstruct ata_dmasetup_data_cb_args {
19793882Ssos    struct ata_dmaentry *dmatab;
19893882Ssos    int error;
19993882Ssos};
20093882Ssos
20193882Ssosstatic void
20293882Ssosata_dmasetupd_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
20393882Ssos{
20493882Ssos    struct ata_dmasetup_data_cb_args *cba =
20593882Ssos	(struct ata_dmasetup_data_cb_args *)xsc;
20693882Ssos    bus_size_t cnt;
20793882Ssos    u_int32_t lastcount;
20893882Ssos    int i, j;
20993882Ssos
21093882Ssos    cba->error = error;
21193882Ssos    if (error != 0)
21293882Ssos	return;
21393882Ssos    lastcount = j = 0;
21493882Ssos    for (i = 0; i < nsegs; i++) {
21593882Ssos	/*
21693882Ssos	 * A maximum segment size was specified for bus_dma_tag_create, but
21793882Ssos	 * some busdma code does not seem to honor this, so fix up if needed.
21893882Ssos	 */
21993882Ssos	for (cnt = 0; cnt < segs[i].ds_len; cnt += MAXSEGSZ, j++) {
22093882Ssos	    cba->dmatab[j].base = htole32(segs[i].ds_addr + cnt);
22193882Ssos	    lastcount = ulmin(segs[i].ds_len - cnt, MAXSEGSZ) & 0xffff;
22293882Ssos	    cba->dmatab[j].count = htole32(lastcount);
22393882Ssos	}
22493882Ssos    }
22593882Ssos    cba->dmatab[j - 1].count = htole32(lastcount | ATA_DMA_EOT);
22693882Ssos}
22793882Ssos
228111188Ssosstatic int
22993882Ssosata_dmasetup(struct ata_device *atadev, caddr_t data, int32_t count)
23045095Ssos{
23193882Ssos    struct ata_channel *ch = atadev->channel;
23245095Ssos
233111188Ssos    if (((uintptr_t)data & (ch->dma->alignment - 1)) ||
234111188Ssos	(count & (ch->dma->alignment - 1))) {
23593882Ssos	ata_prtdev(atadev, "non aligned DMA transfer attempted\n");
23645095Ssos	return -1;
23766070Ssos    }
23845095Ssos
23945095Ssos    if (!count) {
24093882Ssos	ata_prtdev(atadev, "zero length DMA transfer attempted\n");
24145095Ssos	return -1;
24245095Ssos    }
24395010Ssos    return 0;
24495010Ssos}
24545095Ssos
24695010Ssosint
24795010Ssosata_dmastart(struct ata_device *atadev, caddr_t data, int32_t count, int dir)
24895010Ssos{
24995010Ssos    struct ata_channel *ch = atadev->channel;
25095010Ssos    struct ata_dmastate *ds = &atadev->dmastate;
25195010Ssos    struct ata_dmasetup_data_cb_args cba;
25295010Ssos
25395010Ssos    if (ds->flags & ATA_DS_ACTIVE)
25495010Ssos	    panic("ata_dmasetup: transfer active on this device!");
25595010Ssos
25693882Ssos    cba.dmatab = ds->dmatab;
25795010Ssos    bus_dmamap_sync(ds->cdmatag, ds->cdmamap, BUS_DMASYNC_PREWRITE);
25893882Ssos    if (bus_dmamap_load(ds->ddmatag, ds->ddmamap, data, count,
25993882Ssos			ata_dmasetupd_cb, &cba, 0) || cba.error)
26095010Ssos	return -1;
26193882Ssos
26295010Ssos    bus_dmamap_sync(ds->cdmatag, ds->cdmamap, BUS_DMASYNC_POSTWRITE);
26393882Ssos    bus_dmamap_sync(ds->ddmatag, ds->ddmamap, dir ? BUS_DMASYNC_PREREAD :
26493882Ssos		    BUS_DMASYNC_PREWRITE);
26595010Ssos
26695010Ssos    ch->flags |= ATA_DMA_ACTIVE;
267108931Ssos    ds->flags = dir ? (ATA_DS_ACTIVE | ATA_DS_READ) : ATA_DS_ACTIVE;
26895010Ssos
26993882Ssos    ATA_OUTL(ch->r_bmio, ATA_BMDTP_PORT, ds->mdmatab);
27090215Ssos    ATA_OUTB(ch->r_bmio, ATA_BMCMD_PORT, dir ? ATA_BMCMD_WRITE_READ : 0);
27190215Ssos    ATA_OUTB(ch->r_bmio, ATA_BMSTAT_PORT,
272108931Ssos	     (ATA_INB(ch->r_bmio, ATA_BMSTAT_PORT) |
273108931Ssos	     (ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR)));
27490215Ssos    ATA_OUTB(ch->r_bmio, ATA_BMCMD_PORT,
275108931Ssos	     ATA_INB(ch->r_bmio, ATA_BMCMD_PORT) | ATA_BMCMD_START_STOP);
27695010Ssos    return 0;
27745095Ssos}
27845095Ssos
27966070Ssosint
280111188Ssosata_dmastop(struct ata_device *atadev)
28145095Ssos{
282108931Ssos    struct ata_channel *ch = atadev->channel;
283108931Ssos    struct ata_dmastate *ds = &atadev->dmastate;
28472106Ssos    int error;
28572106Ssos
286109529Ssos    error = ATA_INB(ch->r_bmio, ATA_BMSTAT_PORT);
287109529Ssos    ATA_OUTB(ch->r_bmio, ATA_BMCMD_PORT,
288109529Ssos	     ATA_INB(ch->r_bmio, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP);
289109529Ssos    ATA_OUTB(ch->r_bmio, ATA_BMSTAT_PORT,ATA_BMSTAT_INTERRUPT|ATA_BMSTAT_ERROR);
290109529Ssos
29193882Ssos    bus_dmamap_sync(ds->ddmatag, ds->ddmamap, (ds->flags & ATA_DS_READ) != 0 ?
29293882Ssos		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
29393882Ssos    bus_dmamap_unload(ds->ddmatag, ds->ddmamap);
294109529Ssos
29595010Ssos    ch->flags &= ~ATA_DMA_ACTIVE;
29693882Ssos    ds->flags = 0;
29795010Ssos    return (error & ATA_BMSTAT_MASK);
29845095Ssos}
29945095Ssos
300111188Ssosstatic int
30190215Ssosata_dmastatus(struct ata_channel *ch)
30245095Ssos{
30390215Ssos    return ATA_INB(ch->r_bmio, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK;
30445095Ssos}
305