ata-dma.c revision 178114
1/*-
2 * Copyright (c) 1998 - 2008 S�ren Schmidt <sos@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/ata/ata-dma.c 178114 2008-04-11 11:30:27Z sos $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/ata.h>
33#include <sys/kernel.h>
34#include <sys/endian.h>
35#include <sys/malloc.h>
36#include <sys/lock.h>
37#include <sys/sema.h>
38#include <sys/taskqueue.h>
39#include <vm/uma.h>
40#include <sys/bus.h>
41#include <machine/bus.h>
42#include <sys/rman.h>
43#include <dev/pci/pcivar.h>
44#include <dev/ata/ata-all.h>
45#include <dev/ata/ata-pci.h>
46
47/* prototypes */
48static void ata_dmaalloc(device_t dev);
49static void ata_dmafree(device_t dev);
50static void ata_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
51static int ata_dmaload(struct ata_request *request, void *addr, int *nsegs);
52static int ata_dmaunload(struct ata_request *request);
53
54/* local vars */
55static MALLOC_DEFINE(M_ATADMA, "ata_dma", "ATA driver DMA");
56
57/* misc defines */
58#define MAXTABSZ        PAGE_SIZE
59#define MAXWSPCSZ       PAGE_SIZE*2
60
61struct ata_dc_cb_args {
62    bus_addr_t maddr;
63    int error;
64};
65
66void
67ata_dmainit(device_t dev)
68{
69    struct ata_channel *ch = device_get_softc(dev);
70
71    ch->dma.alloc = ata_dmaalloc;
72    ch->dma.free = ata_dmafree;
73    ch->dma.setprd = ata_dmasetprd;
74    ch->dma.load = ata_dmaload;
75    ch->dma.unload = ata_dmaunload;
76    ch->dma.alignment = 2;
77    ch->dma.boundary = 65536;
78    ch->dma.segsize = 63536;
79    ch->dma.max_iosize = 128 * DEV_BSIZE;
80    ch->dma.max_address = BUS_SPACE_MAXADDR_32BIT;
81}
82
83static void
84ata_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
85{
86    struct ata_dc_cb_args *dcba = (struct ata_dc_cb_args *)xsc;
87
88    if (!(dcba->error = error))
89	dcba->maddr = segs[0].ds_addr;
90}
91
92static void
93ata_dmaalloc(device_t dev)
94{
95    struct ata_channel *ch = device_get_softc(dev);
96    struct ata_dc_cb_args dcba;
97
98    if (bus_dma_tag_create(bus_get_dma_tag(dev), ch->dma.alignment, 0,
99			   ch->dma.max_address, BUS_SPACE_MAXADDR,
100			   NULL, NULL, ch->dma.max_iosize,
101			   ATA_DMA_ENTRIES, ch->dma.segsize,
102			   0, NULL, NULL, &ch->dma.dmatag))
103	goto error;
104
105    if (bus_dma_tag_create(ch->dma.dmatag, PAGE_SIZE, 64 * 1024,
106			   ch->dma.max_address, BUS_SPACE_MAXADDR,
107			   NULL, NULL, MAXWSPCSZ, 1, MAXWSPCSZ,
108			   0, NULL, NULL, &ch->dma.work_tag))
109	goto error;
110
111    if (bus_dmamem_alloc(ch->dma.work_tag, (void **)&ch->dma.work, 0,
112			 &ch->dma.work_map))
113	goto error;
114
115    if (bus_dmamap_load(ch->dma.work_tag, ch->dma.work_map ,ch->dma.work,
116			MAXWSPCSZ, ata_dmasetupc_cb, &dcba, BUS_DMA_NOWAIT) ||
117			dcba.error) {
118	bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map);
119	goto error;
120    }
121    ch->dma.work_bus = dcba.maddr;
122
123    return;
124
125error:
126    device_printf(dev, "WARNING - DMA allocation failed, disabling DMA\n");
127    ata_dmafree(dev);
128}
129
130static void
131ata_dmafree(device_t dev)
132{
133    struct ata_channel *ch = device_get_softc(dev);
134
135    if (ch->dma.work_bus) {
136	bus_dmamap_unload(ch->dma.work_tag, ch->dma.work_map);
137	bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map);
138	ch->dma.work_bus = 0;
139	ch->dma.work_map = NULL;
140	ch->dma.work = NULL;
141    }
142    if (ch->dma.work_tag) {
143	bus_dma_tag_destroy(ch->dma.work_tag);
144	ch->dma.work_tag = NULL;
145    }
146    if (ch->dma.dmatag) {
147	bus_dma_tag_destroy(ch->dma.dmatag);
148	ch->dma.dmatag = NULL;
149    }
150}
151
152static void
153ata_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
154{
155    struct ata_dmasetprd_args *args = xsc;
156    struct ata_dma_prdentry *prd = args->dmatab;
157    int i;
158
159    if ((args->error = error))
160	return;
161
162    for (i = 0; i < nsegs; i++) {
163	prd[i].addr = htole32(segs[i].ds_addr);
164	prd[i].count = htole32(segs[i].ds_len);
165    }
166    prd[i - 1].count |= htole32(ATA_DMA_EOT);
167    KASSERT(nsegs <= ATA_DMA_ENTRIES, ("too many DMA segment entries\n"));
168    args->nsegs = nsegs;
169}
170
171static int
172ata_dmaload(struct ata_request *request, void *addr, int *entries)
173{
174    struct ata_channel *ch = device_get_softc(request->parent);
175    struct ata_dc_cb_args dcba;
176    struct ata_dmasetprd_args dspa;
177    int error;
178
179    ATA_DEBUG_RQ(request, "dmaload");
180
181    if (request->dma.cur_iosize) {
182	device_printf(request->dev,
183		      "FAILURE - already active DMA on this device\n");
184	return EIO;
185    }
186    if (!request->bytecount) {
187	device_printf(request->dev,
188		      "FAILURE - zero length DMA transfer attempted\n");
189	return EIO;
190    }
191    if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) ||
192	(request->bytecount & (ch->dma.alignment - 1))) {
193	device_printf(request->dev,
194		      "FAILURE - non aligned DMA transfer attempted\n");
195	return EIO;
196    }
197    if (request->bytecount > ch->dma.max_iosize) {
198	device_printf(request->dev,
199		      "FAILURE - oversized DMA transfer attempt %d > %d\n",
200		      request->bytecount, ch->dma.max_iosize);
201	return EIO;
202    }
203
204    if (bus_dmamap_load(request->dma.sg_tag, request->dma.sg_map,
205			request->dma.sg, MAXTABSZ,
206			ata_dmasetupc_cb, &dcba, BUS_DMA_NOWAIT) || dcba.error){
207	bus_dmamem_free(request->dma.sg_tag,
208			request->dma.sg, request->dma.sg_map);
209	device_printf(request->dev, "FAILURE - load sg\n");
210	goto error;
211    }
212    request->dma.sg_bus = dcba.maddr;
213
214    if (bus_dmamap_create(request->dma.data_tag, 0, &request->dma.data_map)) {
215	device_printf(request->dev, "FAILURE - create data_map\n");
216	goto error;
217    }
218
219    if (addr)
220	dspa.dmatab = addr;
221    else
222	dspa.dmatab = request->dma.sg;
223
224    if ((error = bus_dmamap_load(request->dma.data_tag, request->dma.data_map,
225				 request->data, request->bytecount,
226				 ch->dma.setprd, &dspa, BUS_DMA_NOWAIT)) ||
227				 (error = dspa.error)) {
228	device_printf(request->dev, "FAILURE - load data\n");
229	goto error;
230    }
231
232    if (entries)
233	*entries = dspa.nsegs;
234
235    bus_dmamap_sync(request->dma.sg_tag, request->dma.sg_map,
236		    BUS_DMASYNC_PREWRITE);
237
238    bus_dmamap_sync(request->dma.data_tag, request->dma.data_map,
239		    (request->flags & ATA_R_READ) ?
240		    BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
241
242    request->dma.cur_iosize = request->bytecount;
243
244    return 0;
245
246error:
247    ata_dmaunload(request);
248    return EIO;
249}
250
251int
252ata_dmaunload(struct ata_request *request)
253{
254    ATA_DEBUG_RQ(request, "dmaunload");
255
256    if (request->dma.cur_iosize) {
257	bus_dmamap_sync(request->dma.sg_tag, request->dma.sg_map,
258			BUS_DMASYNC_POSTWRITE);
259
260	bus_dmamap_sync(request->dma.data_tag, request->dma.data_map,
261			(request->flags & ATA_R_READ) ?
262			BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
263	bus_dmamap_unload(request->dma.data_tag, request->dma.data_map);
264
265	request->dma.cur_iosize = 0;
266    }
267
268    if (request->dma.data_map) {
269	bus_dmamap_destroy(request->dma.data_tag, request->dma.data_map);
270	request->dma.data_map = NULL;
271    }
272
273    if (request->dma.sg_bus) {
274	bus_dmamap_unload(request->dma.sg_tag, request->dma.sg_map);
275	bus_dmamem_free(request->dma.sg_tag, request->dma.sg,
276			request->dma.sg_map);
277	request->dma.sg = NULL;
278	request->dma.sg_bus = 0;
279	request->dma.sg_map = NULL;
280    }
281
282    return 0;
283}
284