ata-dma.c revision 144800
145095Ssos/*-
2144330Ssos * Copyright (c) 1998 - 2005 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 */
2845095Ssos
29119418Sobrien#include <sys/cdefs.h>
30119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ata/ata-dma.c 144800 2005-04-08 12:16:51Z sos $");
31119418Sobrien
3245095Ssos#include <sys/param.h>
3345095Ssos#include <sys/systm.h>
3474302Ssos#include <sys/ata.h>
35111188Ssos#include <sys/kernel.h>
3695533Smike#include <sys/endian.h>
3745095Ssos#include <sys/malloc.h>
38117126Sscottl#include <sys/lock.h>
39124403Ssos#include <sys/sema.h>
40119404Ssos#include <sys/taskqueue.h>
41124534Ssos#include <vm/uma.h>
4245798Ssos#include <sys/bus.h>
4366106Ssos#include <machine/bus.h>
4472106Ssos#include <sys/rman.h>
45119404Ssos#include <dev/pci/pcivar.h>
4645095Ssos#include <dev/ata/ata-all.h>
47111188Ssos#include <dev/ata/ata-pci.h>
4845095Ssos
4952067Ssos/* prototypes */
50119453Ssosstatic void ata_dmaalloc(struct ata_channel *);
51112791Ssosstatic void ata_dmafree(struct ata_channel *);
52133637Ssosstatic void ata_dmasetprd(void *, bus_dma_segment_t *, int, int);
53121310Ssosstatic int ata_dmaload(struct ata_device *, caddr_t, int32_t, int);
54121310Ssosstatic int ata_dmaunload(struct ata_channel *);
5552067Ssos
56111188Ssos/* local vars */
57111188Ssosstatic MALLOC_DEFINE(M_ATADMA, "ATA DMA", "ATA driver DMA");
58111188Ssos
5952067Ssos/* misc defines */
60144330Ssos#define MAXTABSZ        PAGE_SIZE
61144330Ssos#define MAXWSPCSZ       PAGE_SIZE
6245720Speter
6393882Ssosstruct ata_dc_cb_args {
6493882Ssos    bus_addr_t maddr;
6593882Ssos    int error;
6693882Ssos};
6793882Ssos
68119453Ssosvoid
69111188Ssosata_dmainit(struct ata_channel *ch)
70111188Ssos{
71119453Ssos    if ((ch->dma = malloc(sizeof(struct ata_dma), M_ATADMA, M_NOWAIT|M_ZERO))) {
72119453Ssos	ch->dma->alloc = ata_dmaalloc;
73119453Ssos	ch->dma->free = ata_dmafree;
74133637Ssos	ch->dma->setprd = ata_dmasetprd;
75121310Ssos	ch->dma->load = ata_dmaload;
76121310Ssos	ch->dma->unload = ata_dmaunload;
77119453Ssos	ch->dma->alignment = 2;
78135034Ssos	ch->dma->max_iosize = 128 * DEV_BSIZE;
79135034Ssos	ch->dma->boundary = 128 * DEV_BSIZE;
80119453Ssos    }
81111188Ssos}
82111188Ssos
8393882Ssosstatic void
8493882Ssosata_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
8566070Ssos{
8693882Ssos    struct ata_dc_cb_args *cba = (struct ata_dc_cb_args *)xsc;
8766070Ssos
8893882Ssos    if (!(cba->error = error))
8993882Ssos	cba->maddr = segs[0].ds_addr;
9093882Ssos}
9193882Ssos
92119453Ssosstatic void
93112791Ssosata_dmaalloc(struct ata_channel *ch)
9493882Ssos{
9593882Ssos    struct ata_dc_cb_args ccba;
9693882Ssos
97134090Ssos    if (bus_dma_tag_create(NULL, ch->dma->alignment, 0,
98120883Ssos			   BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
99135034Ssos			   NULL, NULL, 256 * DEV_BSIZE,
100134090Ssos			   ATA_DMA_ENTRIES, ch->dma->max_iosize,
101137809Sscottl			   0, NULL, NULL, &ch->dma->dmatag))
102119453Ssos	goto error;
103119453Ssos
104120883Ssos    if (bus_dma_tag_create(ch->dma->dmatag, PAGE_SIZE, PAGE_SIZE,
105119453Ssos			   BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
106120883Ssos			   NULL, NULL, MAXTABSZ, 1, MAXTABSZ,
107144330Ssos			   0, NULL, NULL, &ch->dma->sg_tag))
108119453Ssos	goto error;
109119453Ssos
110123034Ssos    if (bus_dma_tag_create(ch->dma->dmatag,ch->dma->alignment,ch->dma->boundary,
111119453Ssos			   BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
112135034Ssos			   NULL, NULL, 256 * DEV_BSIZE,
113134090Ssos			   ATA_DMA_ENTRIES, ch->dma->max_iosize,
114144330Ssos			   BUS_DMA_ALLOCNOW, NULL, NULL, &ch->dma->data_tag))
115119453Ssos	goto error;
116119453Ssos
117144330Ssos    if (bus_dmamem_alloc(ch->dma->sg_tag, (void **)&ch->dma->sg, 0,
118144330Ssos			 &ch->dma->sg_map))
119119453Ssos	goto error;
120119453Ssos
121144330Ssos    if (bus_dmamap_load(ch->dma->sg_tag, ch->dma->sg_map, ch->dma->sg,
122119453Ssos			MAXTABSZ, ata_dmasetupc_cb, &ccba, 0) || ccba.error) {
123144330Ssos	bus_dmamem_free(ch->dma->sg_tag, ch->dma->sg, ch->dma->sg_map);
124119453Ssos	goto error;
125112791Ssos    }
126144330Ssos    ch->dma->sg_bus = ccba.maddr;
127128183Ssos
128144330Ssos    if (bus_dmamap_create(ch->dma->data_tag, 0, &ch->dma->data_map))
129119453Ssos	goto error;
130128183Ssos
131128183Ssos    if (bus_dma_tag_create(ch->dma->dmatag, PAGE_SIZE, PAGE_SIZE,
132128183Ssos			   BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
133128183Ssos			   NULL, NULL, MAXWSPCSZ, 1, MAXWSPCSZ,
134144330Ssos			   0, NULL, NULL, &ch->dma->work_tag))
135128183Ssos	goto error;
136128183Ssos
137144330Ssos    if (bus_dmamem_alloc(ch->dma->work_tag, (void **)&ch->dma->work, 0,
138144330Ssos			 &ch->dma->work_map))
139128183Ssos	goto error;
140128183Ssos
141144330Ssos    if (bus_dmamap_load(ch->dma->work_tag, ch->dma->work_map,ch->dma->work,
142128183Ssos			MAXWSPCSZ, ata_dmasetupc_cb, &ccba, 0) || ccba.error) {
143144330Ssos	bus_dmamem_free(ch->dma->work_tag,ch->dma->work, ch->dma->work_map);
144128183Ssos	goto error;
145128183Ssos    }
146144330Ssos    ch->dma->work_bus = ccba.maddr;
147128183Ssos
148119453Ssos    return;
14993882Ssos
150119453Ssoserror:
151144330Ssos    device_printf(ch->dev, "WARNING - DMA allocation failed, disabling DMA\n");
152119453Ssos    ata_dmafree(ch);
153119453Ssos    free(ch->dma, M_ATADMA);
154119453Ssos    ch->dma = NULL;
15566070Ssos}
15666070Ssos
157111188Ssosstatic void
158112791Ssosata_dmafree(struct ata_channel *ch)
15945095Ssos{
160144330Ssos    if (ch->dma->work_bus) {
161144330Ssos	bus_dmamap_unload(ch->dma->work_tag, ch->dma->work_map);
162144330Ssos	bus_dmamem_free(ch->dma->work_tag, ch->dma->work, ch->dma->work_map);
163144330Ssos	ch->dma->work_bus = 0;
164144330Ssos	ch->dma->work_map = NULL;
165144330Ssos	ch->dma->work = NULL;
166128183Ssos    }
167144330Ssos    if (ch->dma->work_tag) {
168144330Ssos	bus_dma_tag_destroy(ch->dma->work_tag);
169144330Ssos	ch->dma->work_tag = NULL;
170128183Ssos    }
171144330Ssos    if (ch->dma->sg_bus) {
172144330Ssos	bus_dmamap_unload(ch->dma->sg_tag, ch->dma->sg_map);
173144330Ssos	bus_dmamem_free(ch->dma->sg_tag, ch->dma->sg, ch->dma->sg_map);
174144330Ssos	ch->dma->sg_bus = 0;
175144330Ssos	ch->dma->sg_map = NULL;
176144330Ssos	ch->dma->sg = NULL;
17793882Ssos    }
178144330Ssos    if (ch->dma->data_map) {
179144330Ssos	bus_dmamap_destroy(ch->dma->data_tag, ch->dma->data_map);
180144330Ssos	ch->dma->data_map = NULL;
18193882Ssos    }
182144330Ssos    if (ch->dma->sg_tag) {
183144330Ssos	bus_dma_tag_destroy(ch->dma->sg_tag);
184144330Ssos	ch->dma->sg_tag = NULL;
18593882Ssos    }
186144330Ssos    if (ch->dma->data_tag) {
187144330Ssos	bus_dma_tag_destroy(ch->dma->data_tag);
188144330Ssos	ch->dma->data_tag = NULL;
18993882Ssos    }
190112791Ssos    if (ch->dma->dmatag) {
191112791Ssos	bus_dma_tag_destroy(ch->dma->dmatag);
192112791Ssos	ch->dma->dmatag = NULL;
19393882Ssos    }
19493882Ssos}
19593882Ssos
19693882Ssosstatic void
197133637Ssosata_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
19893882Ssos{
199133637Ssos    struct ata_dmasetprd_args *args = xsc;
200133637Ssos    struct ata_dma_prdentry *prd = args->dmatab;
201133637Ssos    int i;
20293882Ssos
203133637Ssos    if ((args->error = error))
20493882Ssos	return;
205133637Ssos
20693882Ssos    for (i = 0; i < nsegs; i++) {
207133637Ssos	prd[i].addr = htole32(segs[i].ds_addr);
208133637Ssos	prd[i].count = htole32(segs[i].ds_len);
20993882Ssos    }
210133637Ssos    prd[i - 1].count |= htole32(ATA_DMA_EOT);
21193882Ssos}
21293882Ssos
213111188Ssosstatic int
214121310Ssosata_dmaload(struct ata_device *atadev, caddr_t data, int32_t count, int dir)
21545095Ssos{
216144330Ssos    struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev));
217133637Ssos    struct ata_dmasetprd_args cba;
21845095Ssos
219133637Ssos    if (ch->dma->flags & ATA_DMA_LOADED) {
220144330Ssos	device_printf(atadev->dev,
221144330Ssos		      "FAILURE - already active DMA on this device\n");
22245095Ssos	return -1;
22366070Ssos    }
22445095Ssos    if (!count) {
225144330Ssos	device_printf(atadev->dev,
226144330Ssos		      "FAILURE - zero length DMA transfer attempted\n");
22745095Ssos	return -1;
22845095Ssos    }
229121310Ssos    if (((uintptr_t)data & (ch->dma->alignment - 1)) ||
230121310Ssos	(count & (ch->dma->alignment - 1))) {
231144330Ssos	device_printf(atadev->dev,
232144330Ssos		      "FAILURE - non aligned DMA transfer attempted\n");
233121310Ssos	return -1;
234121310Ssos    }
235119404Ssos    if (count > ch->dma->max_iosize) {
236144330Ssos	device_printf(atadev->dev,
237144330Ssos		      "FAILURE - oversized DMA transfer attempted %d > %d\n",
238144330Ssos		      count, ch->dma->max_iosize);
239119404Ssos	return -1;
240119404Ssos    }
24145095Ssos
242144330Ssos    cba.dmatab = ch->dma->sg;
24395010Ssos
244144330Ssos    if (bus_dmamap_load(ch->dma->data_tag, ch->dma->data_map, data, count,
245133637Ssos			ch->dma->setprd, &cba, 0) || cba.error)
24695010Ssos	return -1;
24793882Ssos
248144800Ssos    bus_dmamap_sync(ch->dma->sg_tag, ch->dma->sg_map, BUS_DMASYNC_PREWRITE);
249144800Ssos
250144330Ssos    bus_dmamap_sync(ch->dma->data_tag, ch->dma->data_map,
251112791Ssos		    dir ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
25295010Ssos
253121310Ssos    ch->dma->cur_iosize = count;
254133184Ssos    ch->dma->flags = dir ? (ATA_DMA_LOADED | ATA_DMA_READ) : ATA_DMA_LOADED;
25595010Ssos    return 0;
25645095Ssos}
25745095Ssos
25866070Ssosint
259121310Ssosata_dmaunload(struct ata_channel *ch)
26045095Ssos{
261144330Ssos    bus_dmamap_sync(ch->dma->sg_tag, ch->dma->sg_map, BUS_DMASYNC_POSTWRITE);
262119404Ssos
263144330Ssos    bus_dmamap_sync(ch->dma->data_tag, ch->dma->data_map,
264112791Ssos		    (ch->dma->flags & ATA_DMA_READ) != 0 ?
26593882Ssos		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
266144330Ssos    bus_dmamap_unload(ch->dma->data_tag, ch->dma->data_map);
267119404Ssos
268121310Ssos    ch->dma->cur_iosize = 0;
269133184Ssos    ch->dma->flags &= ~ATA_DMA_LOADED;
270112791Ssos    return 0;
27145095Ssos}
272